//
//  UIViewController+HXNavigationBar.m
//  navibarTest
//
//  Created by 周义进 on 14/09/2018.
//  Copyright © 2018 DaHuanXiong. All rights reserved.
//

#import "UIViewController+HXNavigationBar.h"
#import "HXNavigationBar.h"
#import <objc/runtime.h>

#if 0

#define HXNavLog(...) NSLog(@"%@",[NSString stringWithFormat:__VA_ARGS__])
#else
#define HXNavLog(...)

#endif

@interface NSObject (HXNavigationBar_swizzle)
+ (BOOL)hxNaviBarSwizzle_originalMethod:(SEL)origSel newMethod:(SEL)altSel;
@end

@implementation NSObject (HXNavigationBar_swizzle)

+ (BOOL)hxNaviBarSwizzle_originalMethod:(SEL)origSel newMethod:(SEL)altSel {
    Class class = self;
    Method origMethod = class_getInstanceMethod(class, origSel);
    Method altMethod = class_getInstanceMethod(class, altSel);
    if (!origMethod || !altMethod) {
        return NO;
    }
    BOOL didAddMethod = class_addMethod(class,origSel,
                                        method_getImplementation(altMethod),
                                        method_getTypeEncoding(altMethod));
    
    if (didAddMethod) {
        class_replaceMethod(class,altSel,
                            method_getImplementation(origMethod),
                            method_getTypeEncoding(origMethod));
    } else {
        method_exchangeImplementations(origMethod, altMethod);
    }
    return YES;
}
@end



@interface UINavigationBar (HXNavigationBar)
@property (nonatomic, strong) UIView      *hxBackgroundView;
@property (nonatomic, strong) UIImageView *hxBackgroundImageView;
@property (nonatomic, strong) UIImage     *hxBackgroundImage;

@end

@implementation UINavigationBar (HXNavigationBar)
static char kHXBackgroundViewKey;
static char kHXBackgroundImageViewKey;
static char kHXBackgroundImageKey;

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
        [object_getClass([UINavigationBar new]) hxNaviBarSwizzle_originalMethod:@selector(setTitleTextAttributes:) newMethod:@selector(hx_setTitleTextAttributes:)];
    });
}

#pragma mark - Life Cycle


#pragma mark - System Method

- (void)hx_setTitleTextAttributes:(NSDictionary<NSAttributedStringKey,id> *)titleTextAttributes {
    NSMutableDictionary<NSString *,id> *newTitleTextAttributes = [titleTextAttributes mutableCopy];
    if (newTitleTextAttributes == nil) {
        return;
    }

    NSDictionary<NSString *,id> *originTitleTextAttributes = self.titleTextAttributes;
    if (originTitleTextAttributes == nil) {
        [self hx_setTitleTextAttributes:newTitleTextAttributes];
        return;
    }

    __block UIColor *titleColor;
    [originTitleTextAttributes enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        if ([key isEqual:NSForegroundColorAttributeName]) {
            titleColor = (UIColor *)obj;
            *stop = YES;
        }
    }];

    if (titleColor == nil) {
        [self hx_setTitleTextAttributes:newTitleTextAttributes];
        return;
    }

    if (newTitleTextAttributes[NSForegroundColorAttributeName] == nil) {
        newTitleTextAttributes[NSForegroundColorAttributeName] = titleColor;
    }
    [self hx_setTitleTextAttributes:newTitleTextAttributes];
}

#pragma mark - Public Method

#pragma mark - Private Method


- (CGRect)hx_adjustFrame {
    return  CGRectMake(0, 0, self.frame.size.width, self.frame.origin.y + self.frame.size.height);
}

#pragma mark - set naviBar
- (void)hx_setBackgroundImage:(UIImage *)image {
    [self.hxBackgroundView removeFromSuperview];
    self.hxBackgroundView = nil;
    if (self.hxBackgroundImageView == nil) {
        // add a image(nil color) to _UIBarBackground make it clear
        [self setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
        if (self.subviews.count > 0) {
            self.hxBackgroundImageView = [[UIImageView alloc] initWithFrame:self.hx_adjustFrame];
            self.hxBackgroundImageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
            
            // _UIBarBackground is first subView for navigationBar
            [self.subviews.firstObject insertSubview:self.hxBackgroundImageView atIndex:0];
        }
    }
    self.hxBackgroundImage = image;
    self.hxBackgroundImageView.image = image;
}

- (void)hx_setBackgroundColor:(UIColor *)color {
    [self.hxBackgroundImageView removeFromSuperview];
    self.hxBackgroundImageView = nil;
    self.hxBackgroundImage = nil;
    if (self.hxBackgroundView.superview == nil) {
        // add a image(nil color) to _UIBarBackground make it clear
        [self setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
        
        self.hxBackgroundView.frame = self.hx_adjustFrame;
        
        UIBlurEffect *blurEffrct =[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
        UIVisualEffectView *visualEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffrct];
        visualEffectView.frame = self.hxBackgroundView.frame;
        [visualEffectView.contentView addSubview:self.hxBackgroundView];
        
        // _UIBarBackground is first subView for navigationBar
        [self.subviews.firstObject insertSubview:visualEffectView atIndex:0];
    }
    self.hxBackgroundView.backgroundColor = color;
}

- (void)hx_setBackgroundAlpha:(CGFloat)alpha {
    HXNavLog(@"set alpha:%@", @(alpha));
    UIView *barBackgroundView = self.subviews.firstObject;
    if (@available(iOS 11.0, *))
    {   // sometimes we can't change _UIBarBackground alpha
        for (UIView *view in barBackgroundView.subviews) {
            view.alpha = alpha;
        }
    } else {
        barBackgroundView.alpha = alpha;
    }
    
}

- (void)hx_setBarButtonItemsAlpha:(CGFloat)alpha {
    
    BOOL hasBarBackgroundView = NO;
    for (UIView *view in self.subviews) {
        
        // _UIBarBackground/_UINavigationBarBackground对应的view是系统导航栏，不需要改变其透明度
        if ([view isKindOfClass:NSClassFromString(@"_UIBarBackground")]) {
            hasBarBackgroundView = YES;
            continue;
        }
        
        if ([view isKindOfClass:NSClassFromString(@"_UINavigationBarBackground")]) {
            hasBarBackgroundView = YES;
            continue;
        }
        
        if ([view isKindOfClass:NSClassFromString(@"_UINavigationBarBackIndicatorView")]) {//系统原生返回箭头view
            if (view.alpha <= 0 || view.hidden == YES) {
                continue;
            }
        }
        
        if (hasBarBackgroundView) {
            view.alpha = alpha;
        }
    }
}

//设置导航栏在垂直方向上平移多少距离   CGAffineTransformMakeTranslation  平移
- (void)wr_setTranslationY:(CGFloat)translationY {
    self.transform = CGAffineTransformMakeTranslation(0, translationY);
}

- (CGFloat)wr_getTranslationY {
    return self.transform.ty;
}

#pragma mark - Delegate

#pragma mark - Setter And Getter
- (UIView *)hxBackgroundView {
    UIView *object = (UIView *)objc_getAssociatedObject(self, &kHXBackgroundViewKey);
    if (!object) {
        object = [[UIView alloc] initWithFrame:self.hx_adjustFrame];
        self.hxBackgroundView = object;
    }
    return object;
}

- (void)setHxBackgroundView:(UIView *)hxBackgroundView {
    objc_setAssociatedObject(self, &kHXBackgroundViewKey, hxBackgroundView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}


- (UIImageView *)hxBackgroundImageView {
    return (UIImageView *)objc_getAssociatedObject(self, &kHXBackgroundImageViewKey);
}

- (void)setHxBackgroundImageView:(UIImageView *)hxBackgroundImageView {
    objc_setAssociatedObject(self, &kHXBackgroundImageViewKey, hxBackgroundImageView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (UIImage *)hxBackgroundImage {
    return (UIImage *)objc_getAssociatedObject(self, &kHXBackgroundImageKey);
}

- (void)setHxBackgroundImage:(UIImage *)hxBackgroundImage {
    objc_setAssociatedObject(self, &kHXBackgroundImageKey, hxBackgroundImage, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

#pragma mark - Dealloc

@end

@implementation UINavigationController (HXNavigationBar)

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        UINavigationController *nav = [UINavigationController new];
        [object_getClass(nav) hxNaviBarSwizzle_originalMethod:@selector(pushViewController:animated:) newMethod:@selector(hx_pushViewController:animated:)];
        
        [object_getClass(nav) hxNaviBarSwizzle_originalMethod:@selector(popToViewController:animated:) newMethod:@selector(hx_popToViewController:animated:)];
        
        [object_getClass(nav) hxNaviBarSwizzle_originalMethod:@selector(popToRootViewControllerAnimated:) newMethod:@selector(hx_popToRootViewControllerAnimated:)];
        
        [object_getClass(nav) hxNaviBarSwizzle_originalMethod:NSSelectorFromString(@"_updateInteractiveTransition:") newMethod:@selector(hx_updateInteractiveTransition:)];
    });
}

#pragma mark - system method
#pragma mark - deal the gesture of return
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item {
    
    __weak typeof (self) weakSelf = self;
    id<UIViewControllerTransitionCoordinator> coor = [self.topViewController transitionCoordinator];
    
    if (![HXNavigationBar hx_canUsedInNavVC:self]) {
        if ([coor initiallyInteractive] != YES) {
            [self hx_popViewControllerAnimated:YES];
        }
        return YES;
    }
    
    
    if ([coor initiallyInteractive] == YES) {
        
        if (@available(iOS 10.0, *)) {
            [coor notifyWhenInteractionChangesUsingBlock:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
                __strong typeof (self) pThis = weakSelf;
                [pThis dealInteractionChanges:context];
            }];
        } else {
            [coor notifyWhenInteractionEndsUsingBlock:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
                __strong typeof (self) pThis = weakSelf;
                [pThis dealInteractionChanges:context];
            }];
        }
        return YES;
    }
    if (self.hx_transitioning) {
        return NO;
    }
    HXNavLog(@"allow pop");
    [self hx_popViewControllerAnimated:YES];
    return YES;
}

#pragma mark replace system method

- (void)hx_pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
    
    if (self.hx_transitioning) {
        return;
    }
    
    [self hx_setTransitioning:YES];
    
    if (![HXNavigationBar hx_canUsedInNavVC:self]) {
        [self hx_pushViewController:viewController animated:animated];
        return;
    }
    
    if ([viewController hx_navigationBar_Hidden]) {
        return;
    }
    
    __block CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(hx_pushNeedDisplay)];
    [displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
    [CATransaction setCompletionBlock:^{
        [displayLink invalidate];
        displayLink = nil;
        hxPushDisplayCount = 0;
    }];
    [CATransaction setAnimationDuration:hxPushDuration];
    [CATransaction begin];
    [self hx_pushViewController:viewController animated:animated];
    [CATransaction commit];
}

- (NSArray<UIViewController *> *)hx_popToViewController:(UIViewController *)viewController animated:(BOOL)animated {
    
    [self hx_setTransitioning:YES];
    
    if (![HXNavigationBar hx_canUsedInNavVC:self] || [viewController hx_navigationBar_Hidden]) {
        return [self hx_popToViewController:viewController animated:animated];
    }

    
    __block CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(hx_popNeedDisplay)];
    [displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
    [CATransaction setCompletionBlock:^{
        [displayLink invalidate];
        displayLink = nil;
        hxPopDisplayCount = 0;
    }];
    [CATransaction setAnimationDuration:hxPopDuration];
    [CATransaction begin];
    NSArray<UIViewController *> *vcs = [self hx_popToViewController:viewController animated:animated];
    [CATransaction commit];
    return vcs;
}

- (NSArray<UIViewController *> *)hx_popToRootViewControllerAnimated:(BOOL)animated {
    
    [self hx_setTransitioning:YES];
    
    if (![HXNavigationBar hx_canUsedInNavVC:self] || self.viewControllers[0].hx_navigationBar_Hidden) {
        return [self hx_popToRootViewControllerAnimated:animated];
    }
    
    __block CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(hx_popNeedDisplay)];
    [displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
    [CATransaction setCompletionBlock:^{
        [displayLink invalidate];
        displayLink = nil;
        hxPopDisplayCount = 0;
    }];
    [CATransaction setAnimationDuration:hxPopDuration];
    [CATransaction begin];
    NSArray<UIViewController *> *vcs = [self hx_popToRootViewControllerAnimated:animated];
    [CATransaction commit];
    return vcs;
}


- (void)hx_updateInteractiveTransition:(CGFloat)percentComplete {
    
    if ([HXNavigationBar hx_canUsedInNavVC:self]) {
        UIViewController *fromVC = [self.topViewController.transitionCoordinator viewControllerForKey:UITransitionContextFromViewControllerKey];
        UIViewController *toVC = [self.topViewController.transitionCoordinator viewControllerForKey:UITransitionContextToViewControllerKey];
        [self hx_updateNaviBarFromVC:fromVC toVC:toVC progress:percentComplete ignoreAlpha:NO];
    }
    
    [self hx_updateInteractiveTransition:percentComplete];
}

// change navigationBar barTintColor smooth before push to current VC finished or before pop to current VC finished
- (void)hx_pushNeedDisplay {
    if (!self.hx_transitioning) {
        return;
    }
    HXNavLog(@"hx_pushNeedDisplay");
    if (self.topViewController != nil && self.topViewController.transitionCoordinator != nil) {
        hxPushDisplayCount += 1;
        CGFloat pushProgress = [self hxPushProgress];
        UIViewController *fromVC = [self.topViewController.transitionCoordinator viewControllerForKey:UITransitionContextFromViewControllerKey];
        UIViewController *toVC = [self.topViewController.transitionCoordinator viewControllerForKey:UITransitionContextToViewControllerKey];
        
        BOOL translucentToOpaque = [fromVC hx_navigationBar_backgroundAlpha] == 0 && [toVC hx_navigationBar_backgroundAlpha] > 0;
        
        BOOL opaqueToTranslucent = [fromVC hx_navigationBar_backgroundAlpha] > 0 && [toVC hx_navigationBar_backgroundAlpha] == 0;
        
        BOOL ignoreAlpha = translucentToOpaque || opaqueToTranslucent;
        if (translucentToOpaque) {
            [self hx_updateNaviBar_backgroundAlpha:[toVC hx_navigationBar_backgroundAlpha]];
        }
        
        if (opaqueToTranslucent && pushProgress >= 0.9) {
            [UIView animateWithDuration:0.2
                             animations:^{
                                 [self hx_updateNaviBar_backgroundAlpha:[toVC hx_navigationBar_backgroundAlpha]];
                             }];
            
        }
        
        [self hx_updateNaviBarFromVC:fromVC toVC:toVC progress:pushProgress ignoreAlpha:ignoreAlpha];
    }
}


// change navigationBar barTintColor smooth before pop to current VC finished
- (void)hx_popNeedDisplay {
    if (!self.hx_transitioning) {
        return;
    }
    HXNavLog(@"hx_popNeedDisplay");
    if (self.topViewController != nil && self.topViewController.transitionCoordinator != nil) {
        hxPopDisplayCount += 1;
        CGFloat popProgress = [self hxPopProgress];
        UIViewController *fromVC = [self.topViewController.transitionCoordinator viewControllerForKey:UITransitionContextFromViewControllerKey];
        UIViewController *toVC = [self.topViewController.transitionCoordinator viewControllerForKey:UITransitionContextToViewControllerKey];
        
        BOOL translucentToOpaque = [fromVC hx_navigationBar_backgroundAlpha] == 0 && [toVC hx_navigationBar_backgroundAlpha] > 0;
        
        BOOL opaqueToTranslucent = [fromVC hx_navigationBar_backgroundAlpha] > 0 && [toVC hx_navigationBar_backgroundAlpha] == 0;
        
        BOOL ignoreAlpha = translucentToOpaque || opaqueToTranslucent;
        if (translucentToOpaque) {
            [self hx_updateNaviBar_backgroundAlpha:[toVC hx_navigationBar_backgroundAlpha]];
        }
        
        if (opaqueToTranslucent && popProgress >= 0.9) {
            [self hx_updateNaviBar_backgroundAlpha:[toVC hx_navigationBar_backgroundAlpha]];
//            [UIView animateWithDuration:0.2
//                             animations:^{
//                                 [self hx_updateNaviBar_backgroundAlpha:[toVC hx_navigationBar_backgroundAlpha]];
//                             }];
            
        }
        
        
        [self hx_updateNaviBarFromVC:fromVC toVC:toVC progress:popProgress ignoreAlpha:ignoreAlpha];
    }
}

#pragma mark - private
- (void)hx_popViewControllerAnimated:(BOOL)animated {
    NSUInteger itemCount = self.navigationBar.items.count;
    NSUInteger n = self.viewControllers.count >= itemCount ? 2 : 1;
    UIViewController *popToVC = self.viewControllers[self.viewControllers.count - n];
    [self popToViewController:popToVC animated:YES];
}

static CGFloat hxPopDuration = 0.12;
static int hxPopDisplayCount = 0;
- (CGFloat)hxPopProgress {
    CGFloat all = 60 * hxPopDuration;
    int current = MIN(all, hxPopDisplayCount);
    return current / all;
}

static CGFloat hxPushDuration = 0.10;
static int hxPushDisplayCount = 0;
- (CGFloat)hxPushProgress {
    CGFloat all = 60 * hxPushDuration;
    int current = MIN(all, hxPushDisplayCount);
    return current / all;
}

// deal the gesture of return break off
- (void)dealInteractionChanges:(id<UIViewControllerTransitionCoordinatorContext>)context {
    
    void (^animations) (UITransitionContextViewControllerKey) = ^(UITransitionContextViewControllerKey key){
        UIViewController *vc = [context viewControllerForKey:key];
        UIColor *curColor  = [vc hx_navigationBar_barTintColor];
        UIColor *tintColor = [vc hx_navigationBar_tintColor];
        CGFloat curAlpha   = [vc hx_navigationBar_backgroundAlpha];
        [self hx_updateNaviBar_barTintColor:curColor];
        [self hx_updateNaviBar_tintColor:tintColor];
        [self hx_updateNaviBar_backgroundAlpha:curAlpha];
    };

    // after that, cancel the gesture of return
    if ([context isCancelled] == YES) {
        double cancelDuration = 0;
        [UIView animateWithDuration:cancelDuration animations:^{
            animations(UITransitionContextFromViewControllerKey);
        }];
    } else {
        // after that, finish the gesture of return
        double finishDuration = [context transitionDuration] * (1 - [context percentComplete]);
        [UIView animateWithDuration:finishDuration animations:^{
            animations(UITransitionContextToViewControllerKey);
        }];
    }
}


- (UIStatusBarStyle)preferredStatusBarStyle {
    return [self.topViewController hx_statusBarStyle];
}

+ (UIColor *)hx_middleColorWithFromColor:(UIColor *)fromColor toColor:(UIColor *)toColor percent:(CGFloat)percent {
    CGFloat fromRed = 0;
    CGFloat fromGreen = 0;
    CGFloat fromBlue = 0;
    CGFloat fromAlpha = 0;
    [fromColor getRed:&fromRed green:&fromGreen blue:&fromBlue alpha:&fromAlpha];
    
    CGFloat toRed = 0;
    CGFloat toGreen = 0;
    CGFloat toBlue = 0;
    CGFloat toAlpha = 0;
    [toColor getRed:&toRed green:&toGreen blue:&toBlue alpha:&toAlpha];
    
    CGFloat newRed = fromRed + (toRed - fromRed) * percent;
    CGFloat newGreen = fromGreen + (toGreen - fromGreen) * percent;
    CGFloat newBlue = fromBlue + (toBlue - fromBlue) * percent;
    CGFloat newAlpha = fromAlpha + (toAlpha - fromAlpha) * percent;
    return [UIColor colorWithRed:newRed green:newGreen blue:newBlue alpha:newAlpha];
}

+ (CGFloat)hx_middleAlphaWithFromAlpha:(CGFloat)fromAlpha toAlpha:(CGFloat)toAlpha percent:(CGFloat)percent {
    return fromAlpha + (toAlpha - fromAlpha) * percent;
}

#pragma mark - update

- (void)hx_updateNaviBar_barTintColor:(UIColor *)barTintColor {
    HXNavLog(@"modify barTintColor:%@", barTintColor);
    [self.navigationBar hx_setBackgroundColor:barTintColor];
}

- (void)hx_updateNaviBar_backgroundAlpha:(CGFloat)backgroundAlpha {
    [self.navigationBar hx_setBackgroundAlpha:backgroundAlpha];
}

- (void)hx_updateNaviBar_tintColor:(UIColor *)tintColor {
    self.navigationBar.tintColor = tintColor;
}

- (void)hx_updateNaviBar_shadowImageHidden:(BOOL)hidden {
    self.navigationBar.shadowImage = (hidden == YES) ? [UIImage new] : nil;
}

- (void)hx_updateNaviBar_blurEffectOpen:(BOOL)open {
    if (open) {
        self.navigationBar.hxBackgroundView.alpha = 0.85;
    }
    else {
        self.navigationBar.hxBackgroundView.alpha = 1;
    }
}

- (void)hx_updateNaviBar_titleColor:(UIColor *)titleColor {
    NSDictionary *titleTextAttributes = [self.navigationBar titleTextAttributes];
    if (titleTextAttributes == nil) {
        self.navigationBar.titleTextAttributes = @{NSForegroundColorAttributeName:titleColor};
        return;
    }
    NSMutableDictionary *newTitleTextAttributes = [titleTextAttributes mutableCopy];
    newTitleTextAttributes[NSForegroundColorAttributeName] = titleColor;
    self.navigationBar.titleTextAttributes = newTitleTextAttributes;
}

- (void)hx_updateNaviBarFromVC:(UIViewController *)fromVC toVC:(UIViewController *)toVC progress:(CGFloat)progress ignoreAlpha:(BOOL)ignoreAlpha {
    HXNavLog(@"case hx_updateNaviBarFromVC:%@ toVC:%@", fromVC, toVC);
    // change barTintColor
    UIColor *fromBarTintColor = [fromVC hx_navigationBar_barTintColor];
    UIColor *toBarTintColor = [toVC hx_navigationBar_barTintColor];
    UIColor *newBarTintColor = [UINavigationController hx_middleColorWithFromColor:fromBarTintColor toColor:toBarTintColor percent:progress];
    
    if (toVC.hx_navigationBar_backgroundAlpha ==0) {
        HXNavLog(@"case toVC.hx_navigationBar_backgroundAlpha ==0");
    }
    else if (fromVC.hx_navigationBar_backgroundAlpha ==0) {
        HXNavLog(@"case fromVC.hx_navigationBar_backgroundAlpha ==0");
        [self hx_updateNaviBar_barTintColor:toBarTintColor];
    }
    else {
        HXNavLog(@"case fromVC to toVC");
        [self hx_updateNaviBar_barTintColor:newBarTintColor];
    }
    
   
    
    
    
    // change tintColor
    UIColor *fromTintColor = [fromVC hx_navigationBar_tintColor];
    UIColor *toTintColor = [toVC hx_navigationBar_tintColor];
    UIColor *newTintColor = [UINavigationController hx_middleColorWithFromColor:fromTintColor toColor:toTintColor percent:progress];
    [self hx_updateNaviBar_tintColor:newTintColor];
    
    // change titleColor
    UIColor *fromTitleColor = [fromVC hx_navigationBar_titleColor];
    UIColor *toTitleColor = [toVC hx_navigationBar_titleColor];
    UIColor *newTitleColor = [UINavigationController hx_middleColorWithFromColor:fromTitleColor toColor:toTitleColor percent:progress];
    [self hx_updateNaviBar_titleColor:newTitleColor];
    
    if (ignoreAlpha) {
        return;
    }
    // change alpha
    CGFloat fromBarBackgroundAlpha = [fromVC hx_navigationBar_backgroundAlpha];
    CGFloat toBarBackgroundAlpha = [toVC hx_navigationBar_backgroundAlpha];
    CGFloat newBarBackgroundAlpha = [UINavigationController hx_middleAlphaWithFromAlpha:fromBarBackgroundAlpha toAlpha:toBarBackgroundAlpha percent:progress];
    [self hx_updateNaviBar_backgroundAlpha:newBarBackgroundAlpha];
}

#pragma mark setter getter
static char kHX_TransitioningKey;
- (BOOL)hx_transitioning {
    id transitioning = objc_getAssociatedObject(self, &kHX_TransitioningKey);
    return (transitioning != nil) ? [transitioning boolValue] : NO;
}

- (void)hx_setTransitioning:(BOOL)transitioning {
    objc_setAssociatedObject(self, &kHX_TransitioningKey, @(transitioning), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end


@implementation UIViewController (HXNavigationBar)

static char kHX_NavBar_BarTintColorKey;
static char kHX_NavBar_TintColorKey;
static char kHX_NavBar_TitleColorKey;
static char kHX_StatusBar_StyleKey;
static char kHX_NavBar_BackgroundAlphaKey;
static char kHX_NavBar_ShadowImageHiddenKey;
static char kHX_NavBar_BlurEffectOpenKey;
static char kHX_NavBar_HiddenKey;
static char kHX_VCIsAppearAnimationKey;
static char kHX_VCViewDidAppearKey;
+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        UIViewController *nav = [UIViewController new];
        [object_getClass(nav) hxNaviBarSwizzle_originalMethod:@selector(viewWillAppear:) newMethod:@selector(hxNavBar_viewWillAppear:)];
        
        [object_getClass(nav) hxNaviBarSwizzle_originalMethod:@selector(viewDidAppear:) newMethod:@selector(hxNavBar_viewDidAppear:)];
        
        [object_getClass(nav) hxNaviBarSwizzle_originalMethod:@selector(viewWillDisappear:) newMethod:@selector(hxNavBar_viewWillDisappear:)];
        
    });
}

#pragma mark - System Method


#pragma mark   replace system method
- (void)hxNavBar_viewWillAppear:(BOOL)animated {
    
    [self hx_setVCIsAppearAnimation:YES];
    
    if ([HXNavigationBar hx_canUsedInNavVC:self.navigationController] && self.hx_canUsedHXNaviBar) {
        
        BOOL currentNavBarHidden = self.navigationController.navigationBar.hidden;
        
        if (self.navigationController.topViewController == self) {
            [self.navigationController setNavigationBarHidden:self.hx_navigationBar_Hidden animated:animated];
        }
        
        if (![self hx_navigationBar_Hidden]) {
            [self.navigationController hx_updateNaviBar_blurEffectOpen:[self hx_navigationBar_blurEffectOpen]];
            
            
            [self.navigationController hx_updateNaviBar_titleColor:[self hx_navigationBar_titleColor]];
        }
        
        if (currentNavBarHidden) {
            [self.navigationController hx_updateNaviBar_backgroundAlpha:[self hx_navigationBar_backgroundAlpha]];
        }
        
        
        if (!self.transitionCoordinator.initiallyInteractive) {
            [self.navigationController hx_updateNaviBar_shadowImageHidden:[self hx_navigationBar_shadowImageHidden]];
        }
    }

    [self hxNavBar_viewWillAppear:animated];
}

- (void)hxNavBar_viewDidAppear:(BOOL)animated {
    if ([self hx_VCIsAppearAnimation]) {
        [self hx_setVCIsAppearAnimation:NO];
    }
    [self hx_setVCViewDidAppear:YES];
    
    if ([self hx_canUsedHXNaviBar]) {
        [self.navigationController hx_setTransitioning:NO];
    }
    
    if ([HXNavigationBar hx_canUsedInNavVC:self.navigationController] && self.hx_canUsedHXNaviBar) {
        HXNavLog(@"hxNavBar_viewDidAppear");
        
        if (![self hx_navigationBar_Hidden]) {
            
            [self.navigationController hx_updateNaviBar_barTintColor:[self hx_navigationBar_barTintColor]];
            
            [self.navigationController hx_updateNaviBar_shadowImageHidden:[self hx_navigationBar_shadowImageHidden]];
            
            [self.navigationController hx_updateNaviBar_backgroundAlpha:[self hx_navigationBar_backgroundAlpha]];
        }
        
    }
    
    [self hxNavBar_viewDidAppear:animated];
}

- (void)hxNavBar_viewWillDisappear:(BOOL)animated {
    [self hx_setVCViewDidAppear:NO];
    [self hxNavBar_viewWillDisappear:animated];
}

#pragma mark - Private Method
#pragma mark tool
- (BOOL)hx_canUsedHXNaviBar {
    if ([self.parentViewController isEqual:self.navigationController]) {
        return YES;
    }
    return NO;
}

#pragma mark - Life Cycle

#pragma mark - Public Method
#pragma mark   get
- (UIColor *)hx_navigationBar_barTintColor {
    UIColor *barTintColor = (UIColor *)objc_getAssociatedObject(self, &kHX_NavBar_BarTintColorKey);
    return (barTintColor != nil) ? barTintColor : [HXNavigationBar hx_navigationBar_defaultBarTintColor];
}

- (UIColor *)hx_navigationBar_tintColor {
    UIColor *tintColor = (UIColor *)objc_getAssociatedObject(self, &kHX_NavBar_TintColorKey);
    return (tintColor != nil) ? tintColor : [HXNavigationBar hx_navigationBar_defaultTintColor];
}

- (UIColor *)hx_navigationBar_titleColor {
    UIColor *titleColor = (UIColor *)objc_getAssociatedObject(self, &kHX_NavBar_TitleColorKey);
    return (titleColor != nil) ? titleColor : [HXNavigationBar hx_navigationBar_defaultTitleColor];
}


- (CGFloat)hx_navigationBar_backgroundAlpha {
    id barBackgroundAlpha = objc_getAssociatedObject(self, &kHX_NavBar_BackgroundAlphaKey);
    if ([self hx_navigationBar_Hidden]) {
        return 0;
    }
    return (barBackgroundAlpha != nil) ? [barBackgroundAlpha floatValue] : 1.0;
}

- (UIStatusBarStyle)hx_statusBarStyle {
    id style = objc_getAssociatedObject(self, &kHX_StatusBar_StyleKey);
    return (style != nil) ? [style integerValue] : [HXNavigationBar hx_statusBar_defaultStyle];
}

- (BOOL)hx_navigationBar_shadowImageHidden {
    id hidden = objc_getAssociatedObject(self, &kHX_NavBar_ShadowImageHiddenKey);
    return (hidden != nil) ? [hidden boolValue] : [HXNavigationBar hx_navigationBar_defaultShadowImageHidden];
}

- (BOOL)hx_navigationBar_Hidden {
    id hidden = objc_getAssociatedObject(self, &kHX_NavBar_HiddenKey);
    return (hidden != nil) ? [hidden boolValue] : NO;
}

- (BOOL)hx_navigationBar_blurEffectOpen {
    id open = objc_getAssociatedObject(self, &kHX_NavBar_BlurEffectOpenKey);
    return (open != nil) ? [open boolValue] : [HXNavigationBar hx_navigationBar_defaultBlurEffectOpen];
}

- (BOOL)hx_VCIsAppearAnimation {
    id animation = objc_getAssociatedObject(self, &kHX_VCIsAppearAnimationKey);
    return (animation != nil) ? [animation boolValue] : NO;
}

- (BOOL)hx_VCViewDidAppear {
    id appear = objc_getAssociatedObject(self, &kHX_VCViewDidAppearKey);
    return (appear != nil) ? [appear boolValue] : NO;
}

#pragma mark set
- (void)hx_setNavigationBar_barTintColor:(UIColor *)barTintColor {
    
    if ([HXNavigationBar hx_canUsedInNavVC:self.navigationController] && self.hx_canUsedHXNaviBar) {
        
        objc_setAssociatedObject(self, &kHX_NavBar_BarTintColorKey, barTintColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        
        if (![self hx_VCIsAppearAnimation] && [self hx_VCViewDidAppear]) {
            [self.navigationController hx_updateNaviBar_barTintColor:barTintColor];
        }
        
        
    }
    
}

- (void)hx_setNavigationBar_tintColor:(UIColor *)tintColor {
    
    if ([HXNavigationBar hx_canUsedInNavVC:self.navigationController] && self.hx_canUsedHXNaviBar) {
        
        objc_setAssociatedObject(self, &kHX_NavBar_TintColorKey, tintColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        
        if (![self hx_VCIsAppearAnimation] && [self hx_VCViewDidAppear]) {
            [self.navigationController hx_updateNaviBar_tintColor:tintColor];
        }
        
    }
    
    
}

- (void)hx_setNavigationBar_titleColor:(UIColor *)titleColor {
    if ([HXNavigationBar hx_canUsedInNavVC:self.navigationController] && self.hx_canUsedHXNaviBar) {
        
        objc_setAssociatedObject(self, &kHX_NavBar_TitleColorKey, titleColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        
        if (![self hx_VCIsAppearAnimation] && [self hx_VCViewDidAppear]) {
            [self.navigationController hx_updateNaviBar_titleColor:titleColor];
        }
        
        
    }
    
    
}

- (void)hx_setNavigationBar_backgroundAlpha:(CGFloat)backgroundAlpha {
    
    if ([HXNavigationBar hx_canUsedInNavVC:self.navigationController] && self.hx_canUsedHXNaviBar) {
        
        objc_setAssociatedObject(self, &kHX_NavBar_BackgroundAlphaKey, @(backgroundAlpha), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        
        HXNavLog(@"hx_setNavigationBar_backgroundAlpha:%@", @(backgroundAlpha));
        
        if (![self hx_VCIsAppearAnimation] && [self hx_VCViewDidAppear]) {
            [self.navigationController hx_updateNaviBar_backgroundAlpha:backgroundAlpha];
        }
        
    }
    
    
}

- (void)hx_setStatusBarStyle:(UIStatusBarStyle)style{
    
    if ([HXNavigationBar hx_canUsedInNavVC:self.navigationController] && self.hx_canUsedHXNaviBar) {
        
        objc_setAssociatedObject(self, &kHX_StatusBar_StyleKey, @(style), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        
        [self setNeedsStatusBarAppearanceUpdate];
    }
    
}

- (void)hx_setNavigationBar_shadowImageHidden:(BOOL)hidden {
    
    if ([HXNavigationBar hx_canUsedInNavVC:self.navigationController] && self.hx_canUsedHXNaviBar) {
        
        objc_setAssociatedObject(self, &kHX_NavBar_ShadowImageHiddenKey, @(hidden), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        
        if (![self hx_VCIsAppearAnimation]) {
            [self.navigationController hx_updateNaviBar_shadowImageHidden:hidden];
        }
        
    }
}

- (void)hx_setNavigationBar_BlurEffectOpen:(BOOL)open {
    if ([HXNavigationBar hx_canUsedInNavVC:self.navigationController] && self.hx_canUsedHXNaviBar) {
        
        objc_setAssociatedObject(self, &kHX_NavBar_BlurEffectOpenKey, @(open), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        
        [self.navigationController hx_updateNaviBar_blurEffectOpen:open];
    }
}

- (void)hx_setNavigationBar_Hidden:(BOOL)hidden {
    if (![HXNavigationBar hx_canUsedInNavVC:self.navigationController]) {
        return;
    }
    objc_setAssociatedObject(self, &kHX_NavBar_HiddenKey, @(hidden), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (void)hx_setVCIsAppearAnimation:(BOOL)animating {
    objc_setAssociatedObject(self, &kHX_VCIsAppearAnimationKey, @(animating), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (void)hx_setVCViewDidAppear:(BOOL)appear {
    objc_setAssociatedObject(self, &kHX_VCViewDidAppearKey, @(appear), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}


#pragma mark - Delegate

#pragma mark - Setter And Getter

#pragma mark - Dealloc

@end
